package org.example.climactic.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import java.io.IOException;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

/**
 * 书源接口
 */
@RestController
@RequestMapping("/api/booksource")
public class BookSourceController {
    
    // 书源配置
    private static final Map<String, BookSource> BOOK_SOURCES = new HashMap<String, BookSource>();
    
    static {
        // 初始化默认书源
        Map<String, String> defaultRules = new HashMap<String, String>();
        defaultRules.put("exploreUrl", "/");
        defaultRules.put("searchUrl", "/s?q=");
        defaultRules.put("bookInfo.author", ".dd_box .span7@text");
        defaultRules.put("bookInfo.name", ".name@text");
        defaultRules.put("bookInfo.coverUrl", ".cover img@src");
        defaultRules.put("bookInfo.intro", ".book_about dd@text");
        defaultRules.put("bookInfo.lastChapter", ".oninfo a@text");
        defaultRules.put("bookInfo.tocUrl", ".book_more a@href");
        defaultRules.put("toc.chapterList", ".book_last a");
        defaultRules.put("toc.chapterName", "text");
        defaultRules.put("toc.chapterUrl", "href");
        defaultRules.put("content.content", ".Readarea@html");
        defaultRules.put("content.nextPage", ".Readpage a[2]@href");
        defaultRules.put("toc.pagination", ".page a");
        defaultRules.put("search.bookList", "a[href*=/book/]");
        
        BOOK_SOURCES.put("default", new BookSource("default", "默认书源", "https://m.381d505ad3.xyz", defaultRules));
    }
    
    /**
     * 获取所有书源
     */
    @GetMapping("/sources")
    public Map<String, BookSource> getAllBookSources() {
        return BOOK_SOURCES;
    }
    
    /**
     * 根据ID获取书源
     */
    @GetMapping("/source")
    public BookSource getBookSourceById(@RequestParam String sourceId) {
        return BOOK_SOURCES.get(sourceId);
    }
    
    /**
     * 根据关键字和书源搜索书籍
     */
    @GetMapping("/search")
    @Operation(summary = "搜索书籍", description = "根据关键字和指定书源搜索书籍")
    public String searchBooks(
            @Parameter(description = "搜索关键字") @RequestParam String keyword,
            @Parameter(description = "书源ID") @RequestParam(required = false, defaultValue = "default") String sourceId) throws IOException {
        
        BookSource bookSource = BOOK_SOURCES.get(sourceId);
        if (bookSource == null) {
            return new JSONObject().put("error", "未找到指定书源").toString();
        }
        
        // 验证书源URL
        String baseUrl = bookSource.getBaseUrl();
        try {
            URL url = new URL(baseUrl);
            String host = url.getHost();
            if (host == null || host.isEmpty()) {
                return new JSONObject().put("error", "书源URL格式不正确").toString();
            }
        } catch (Exception e) {
            return new JSONObject().put("error", "书源URL格式不正确: " + e.getMessage()).toString();
        }
        
        // 构造搜索URL
        String searchPath = bookSource.getRule("searchUrl", "/s?q=");
        String searchUrl;
        
        try {
            // 规范化基础URL，移除末尾的斜杠
            if (baseUrl.endsWith("/")) {
                baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
            }
            
            // 处理搜索路径中的参数替换
            if (searchPath.contains("{{key}}") || searchPath.contains("{{page}}")) {
                // 处理模板语法 {{key}} 和 {{page}}
                searchPath = searchPath.replace("{{key}}", URLEncoder.encode(keyword, "UTF-8"))
                                      .replace("{{page}}", "1");
                searchUrl = buildUrl(baseUrl, searchPath);
            } else if (searchPath.contains("{key}") || searchPath.contains("{page}")) {
                // 处理模板语法 {key} 和 {page}
                searchPath = searchPath.replace("{key}", URLEncoder.encode(keyword, "UTF-8"))
                                      .replace("{page}", "1");
                searchUrl = buildUrl(baseUrl, searchPath);
            } else if (searchPath.contains("?")) {
                // 处理标准查询参数
                String encodedKeyword = URLEncoder.encode(keyword, "UTF-8");
                
                // 检查是否包含key或keyword参数占位符
                if (searchPath.contains("key=")) {
                    // 替换key参数占位符
                    searchPath = searchPath.replace("key=", "key=" + encodedKeyword);
                } else if (searchPath.contains("keyword=")) {
                    // 替换keyword参数占位符
                    searchPath = searchPath.replace("keyword=", "keyword=" + encodedKeyword);
                } else {
                    // 添加keyword参数
                    String separator = searchPath.endsWith("?") ? "" : "&";
                    searchPath = searchPath + separator + "keyword=" + encodedKeyword;
                }
                
                searchUrl = buildUrl(baseUrl, searchPath);
            } else {
                // 处理路径参数
                String encodedKeyword = URLEncoder.encode(keyword, "UTF-8");
                searchPath = searchPath + encodedKeyword;
                searchUrl = buildUrl(baseUrl, searchPath);
            }
        } catch (Exception e) {
            return new JSONObject()
                .put("error", "URL编码错误: " + e.getMessage())
                .put("baseUrl", baseUrl)
                .put("searchPath", searchPath)
                .toString();
        }
        
        // 使用HttpClient发送请求
        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpGet request = new HttpGet(searchUrl);
            request.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
            request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
            request.addHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
            request.addHeader("Connection", "keep-alive");
            request.addHeader("Upgrade-Insecure-Requests", "1");
            request.addHeader("Sec-Fetch-Dest", "document");
            request.addHeader("Sec-Fetch-Mode", "navigate");
            request.addHeader("Sec-Fetch-Site", "none");
            request.addHeader("Cache-Control", "max-age=0");
            
            CloseableHttpResponse response = httpClient.execute(request);
            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
            Document doc = Jsoup.parse(result);
            
            // 解析搜索结果
            JSONArray books = new JSONArray();
            // 使用书源配置的搜索结果列表规则
            String bookListSelector = bookSource.getRule("search.bookList", "a[href*=/book/]");
            Elements bookElements = doc.select(bookListSelector);
            
            // 用于去重
            Map<String, Boolean> processedUrls = new HashMap<String, Boolean>();
            
            // 如果使用默认选择器没有找到结果，尝试其他常见选择器
            if (bookElements.isEmpty()) {
                // 尝试多种常见的书籍链接选择器
                String[] fallbackSelectors = {
                    ".bookbox .bookinfo a",
                    ".bookbox a",
                    ".book-list a",
                    ".search-result a",
                    ".item a",
                    "a[href*=/book/]",
                    "a[href*=?bookid=]",
                    "a[href*=book]",
                    ".book a",
                    ".title a",
                    "a[href*=novel]",
                    "a[href*=read]",
                    "a"
                };
                
                for (String selector : fallbackSelectors) {
                    bookElements = doc.select(selector);
                    if (!bookElements.isEmpty()) {
                        bookListSelector = selector + " (fallback)";
                        break;
                    }
                }
            }
            
            // 如果仍然没有找到元素，尝试查找所有链接并进行过滤
            if (bookElements.isEmpty()) {
                Elements allLinks = doc.select("a[href]");
                for (Element link : allLinks) {
                    String href = link.attr("href");
                    String text = link.text().trim();
                    
                    // 判断是否可能是书籍链接
                    if (isLikelyBookLink(href, text)) {
                        bookElements.add(link);
                    }
                }
                
                if (!bookElements.isEmpty()) {
                    bookListSelector = "custom filter (all links filtered)";
                }
            }
            
            for (Element element : bookElements) {
                String bookName = element.text().trim();
                String bookUrl = element.attr("href");
                
                // 更宽松的过滤条件，只过滤明显无效的内容
                if (bookName.isEmpty() || bookName.length() <= 1) {
                    continue;
                }
                
                // 处理URL
                if (bookUrl.startsWith("/")) {
                    bookUrl = bookSource.getBaseUrl() + bookUrl;
                } else if (!bookUrl.startsWith("http")) {
                    bookUrl = bookSource.getBaseUrl() + "/" + bookUrl;
                }
                
                // 验证书籍URL主机名
                try {
                    URL url = new URL(bookUrl);
                    String host = url.getHost();
                    URL base = new URL(bookSource.getBaseUrl());
                    String baseHost = base.getHost();
                    if (!host.equals(baseHost)) {
                        // 跳过非本书源的链接
                        continue;
                    }
                } catch (Exception e) {
                    // URL格式不正确，跳过
                    continue;
                }
                
                // 去重
                if (processedUrls.containsKey(bookUrl)) {
                    continue;
                }
                processedUrls.put(bookUrl, Boolean.TRUE);
                
                JSONObject book = new JSONObject();
                book.put("name", bookName);
                book.put("url", bookUrl);
                
                // 尝试提取更多书籍信息
                Element parent = element.parent();
                if (parent != null) {
                    // 尝试获取作者信息
                    Elements authorElements = parent.select(".author");
                    if (!authorElements.isEmpty()) {
                        book.put("author", authorElements.first().text().trim());
                    }
                    
                    // 尝试获取简介信息
                    Elements introElements = parent.select(".intro");
                    if (!introElements.isEmpty()) {
                        book.put("intro", introElements.first().text().trim());
                    }
                    
                    // 尝试获取封面信息
                    Elements coverElements = parent.select(".cover img");
                    if (!coverElements.isEmpty()) {
                        book.put("coverUrl", coverElements.first().attr("src"));
                    }
                }
                
                books.put(book);
            }
            
            JSONObject resultObj = new JSONObject();
            resultObj.put("books", books);
            resultObj.put("count", books.length());
            resultObj.put("sourceId", sourceId);
            resultObj.put("keyword", keyword);
            resultObj.put("searchUrl", searchUrl);
            resultObj.put("usedSelector", bookListSelector);
            resultObj.put("totalElementsFound", bookElements.size());
            resultObj.put("responseLength", result.length());
            
            // 如果没有找到结果，返回调试信息
            if (books.length() == 0) {
                // 提取页面标题
                Element titleElement = doc.selectFirst("title");
                String title = titleElement != null ? titleElement.text() : "无标题";
                
                // 查找页面中是否包含关键字
                boolean containsKeyword = result.contains(keyword);
                
                // 查找页面中是否包含"搜索"相关词汇
                boolean containsSearchTerm = result.contains("搜索") || result.contains("search") || 
                                           result.contains("result") || result.contains("结果");
                
                // 获取页面中所有链接数量
                Elements allLinks = doc.select("a[href]");
                
                resultObj.put("debug", new JSONObject()
                    .put("pageTitle", title)
                    .put("containsKeyword", containsKeyword)
                    .put("containsSearchTerm", containsSearchTerm)
                    .put("totalLinksInPage", allLinks.size())
                    .put("first500Chars", result.length() > 500 ? result.substring(0, 500) : result));
            }
            
            return resultObj.toString();
        } catch (UnknownHostException e) {
            JSONObject errorResult = new JSONObject();
            errorResult.put("error", "无法解析主机名，请检查书源URL配置是否正确");
            errorResult.put("host", e.getMessage());
            errorResult.put("searchUrl", searchUrl);
            return errorResult.toString();
        } catch (Exception e) {
            JSONObject errorResult = new JSONObject();
            errorResult.put("error", "搜索过程中发生错误: " + e.getMessage());
            errorResult.put("searchUrl", searchUrl);
            return errorResult.toString();
        } finally {
            httpClient.close();
        }
    }
    
    /**
     * 判断链接是否可能是书籍链接
     */
    private boolean isLikelyBookLink(String href, String text) {
        // 过滤明显不是书籍链接的内容
        if (text.isEmpty() || text.length() <= 1) {
            return false;
        }
        
        if (text.contains("书架") || text.contains("首页") || text.contains("记录") || 
            text.contains("书签") || text.contains("登录") || text.contains("注册") ||
            text.equals("null") || text.equals("undefined")) {
            return false;
        }
        
        // 检查链接是否包含书籍相关关键词
        if (href.contains("/book/") || href.contains("bookid=") || href.contains("novel") || 
            href.contains("read") || href.contains("/detail/")) {
            return true;
        }
        
        // 检查文本是否看起来像书名（至少两个字符，且不包含特殊导航词）
        if (text.length() >= 2 && !text.matches(".*[导航|菜单|设置|个人中心].*")) {
            return true;
        }
        
        return false;
    }
    
    /**
     * 构建完整的URL
     * @param baseUrl 基础URL
     * @param path 路径
     * @return 完整的URL
     */
    private String buildUrl(String baseUrl, String path) {
        // 确保baseUrl不以斜杠结尾
        if (baseUrl.endsWith("/")) {
            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
        }
        
        // 处理路径
        if (path.startsWith("http://") || path.startsWith("https://")) {
            // 如果路径已经是完整URL，直接返回
            return path;
        } else if (path.startsWith("/")) {
            // 如果路径以/开头，直接拼接
            return baseUrl + path;
        } else {
            // 其他情况，在baseUrl后添加/再拼接路径
            return baseUrl + "/" + path;
        }
    }
    
    /**
     * 获取远程书源配置
     */
    private JSONObject fetchRemoteConfig(String configUrl) throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        
        try {
            HttpGet request = new HttpGet(configUrl);
            request.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
            
            CloseableHttpResponse response = httpClient.execute(request);
            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
            
            // 解析JSON配置
            JSONArray configs = new JSONArray(result);
            if (configs.length() <= 0) {
                return new JSONObject()
                    .put("error", "配置文件中没有找到有效的书源配置")
                    .put("configUrl", configUrl);
            }
            
            JSONObject config = configs.getJSONObject(0); // 取第一个配置
            
            // 检查必要字段
            if (!config.has("bookSourceUrl") || !config.has("bookSourceName")) {
                return new JSONObject()
                    .put("error", "配置文件缺少必要字段: bookSourceUrl 或 bookSourceName")
                    .put("configUrl", configUrl);
            }
            
            return config;
        } finally {
            httpClient.close();
        }
    }
    
    /**
     * 从URL添加书源
     */
    @GetMapping("/addSourceFromUrl")
    @Operation(summary = "从URL添加书源", description = "从指定URL获取书源配置并添加到系统中")
    public String addSourceFromUrl(@Parameter(description = "书源配置URL") @RequestParam String configUrl) throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        
        try {
            HttpGet request = new HttpGet(configUrl);
            request.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
            
            CloseableHttpResponse response = httpClient.execute(request);
            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
            
            // 验证响应内容是否为空
            if (result == null || result.trim().isEmpty()) {
                return new JSONObject()
                    .put("error", "从URL获取的配置内容为空")
                    .put("configUrl", configUrl)
                    .put("responseCode", response.getStatusLine().getStatusCode())
                    .toString();
            }
            
            // 验证响应内容是否为有效的JSON格式
            result = result.trim();
            if (!result.startsWith("[") && !result.startsWith("{")) {
                return new JSONObject()
                    .put("error", "从URL获取的配置内容不是有效的JSON格式")
                    .put("configUrl", configUrl)
                    .put("responseCode", response.getStatusLine().getStatusCode())
                    .put("preview", result.length() > 200 ? result.substring(0, 200) : result)
                    .toString();
            }
            
            JSONArray configs;
            try {
                // 尝试解析为JSONArray
                if (result.startsWith("[")) {
                    configs = new JSONArray(result);
                } else {
                    // 如果是JSONObject，包装成JSONArray
                    JSONObject configObj = new JSONObject(result);
                    configs = new JSONArray();
                    configs.put(configObj);
                }
            } catch (Exception e) {
                return new JSONObject()
                    .put("error", "解析JSON配置时发生错误: " + e.getMessage())
                    .put("configUrl", configUrl)
                    .put("responseCode", response.getStatusLine().getStatusCode())
                    .put("preview", result.length() > 200 ? result.substring(0, 200) : result)
                    .toString();
            }
            
            if (configs.length() <= 0) {
                return new JSONObject()
                    .put("error", "配置文件中没有找到有效的书源配置")
                    .put("configUrl", configUrl)
                    .toString();
            }
            
            JSONObject config = configs.getJSONObject(0); // 取第一个配置
            
            // 检查必要字段
            if (!config.has("bookSourceUrl") || !config.has("bookSourceName")) {
                return new JSONObject()
                    .put("error", "配置文件缺少必要字段: bookSourceUrl 或 bookSourceName")
                    .put("configUrl", configUrl)
                    .toString();
            }
            
            // 使用bookSourceGroup作为sourceId的前缀，如果没有则使用bookSourceName
            String sourceIdPrefix = config.optString("bookSourceGroup", "");
            if (sourceIdPrefix.isEmpty()) {
                sourceIdPrefix = config.optString("bookSourceName", "unknown_source");
            }
            
            // 清理sourceId，只保留字母、数字、点和下划线
            String sourceId = sourceIdPrefix.replaceAll("[^a-zA-Z0-9._-]", "_");
            
            // 如果sourceId为空或太短，则使用URL生成
            if (sourceId.isEmpty() || sourceId.length() < 3) {
                String baseUrl = config.optString("bookSourceUrl", "unknown_source");
                sourceId = baseUrl.replaceAll("https?://", "")
                        .replaceAll("[^a-zA-Z0-9.-]", "_");
            }
            
            // 确保sourceId不为空
            if (sourceId.isEmpty()) {
                sourceId = "source_" + System.currentTimeMillis();
            }
            
            String sourceName = config.optString("bookSourceName", "未知书源");
            String baseUrl = config.optString("bookSourceUrl", "");
            
            // 验证baseUrl
            if (baseUrl.isEmpty()) {
                return new JSONObject()
                    .put("error", "书源URL不能为空")
                    .put("sourceId", sourceId)
                    .put("sourceName", sourceName)
                    .toString();
            }
            
            // 规范化基础URL，移除末尾的斜杠
            if (baseUrl.endsWith("/")) {
                baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
            }
            
            // 创建规则映射
            Map<String, String> rules = new HashMap<String, String>();
            
            // 处理搜索URL
            if (config.has("searchUrl")) {
                rules.put("searchUrl", getStringValue(config, "searchUrl"));
            }
            
            // 处理发现URL
            if (config.has("exploreUrl")) {
                rules.put("exploreUrl", getStringValue(config, "exploreUrl"));
            }
            
            // 处理搜索结果列表规则
            if (config.has("ruleSearchList")) {
                rules.put("search.bookList", getStringValue(config, "ruleSearchList"));
            }
            
            // 处理书籍信息规则
            if (config.has("ruleBookName")) {
                rules.put("bookInfo.name", getStringValue(config, "ruleBookName"));
            }
            
            if (config.has("ruleBookAuthor")) {
                rules.put("bookInfo.author", getStringValue(config, "ruleBookAuthor"));
            }
            
            if (config.has("ruleBookCoverUrl")) {
                rules.put("bookInfo.coverUrl", getStringValue(config, "ruleBookCoverUrl"));
            }
            
            if (config.has("ruleBookIntro")) {
                rules.put("bookInfo.intro", getStringValue(config, "ruleBookIntro"));
            }
            
            if (config.has("ruleBookLastChapter")) {
                rules.put("bookInfo.lastChapter", getStringValue(config, "ruleBookLastChapter"));
            }
            
            if (config.has("ruleBookTocUrl")) {
                rules.put("bookInfo.tocUrl", getStringValue(config, "ruleBookTocUrl"));
            }
            
            // 处理目录规则
            if (config.has("ruleTocList")) {
                rules.put("toc.chapterList", getStringValue(config, "ruleTocList"));
            }
            
            if (config.has("ruleTocName")) {
                rules.put("toc.chapterName", getStringValue(config, "ruleTocName"));
            }
            
            if (config.has("ruleTocUrl")) {
                rules.put("toc.chapterUrl", getStringValue(config, "ruleTocUrl"));
            }
            
            // 处理内容规则
            if (config.has("ruleContent")) {
                rules.put("content.content", getStringValue(config, "ruleContent"));
            }
            
            if (config.has("ruleContentNext")) {
                rules.put("content.nextPage", getStringValue(config, "ruleContentNext"));
            }
            
            // 处理分页规则
            if (config.has("ruleTocUrlNext")) {
                rules.put("toc.pagination", getStringValue(config, "ruleTocUrlNext"));
            }
            
            // 创建并添加书源
            BookSource bookSource = new BookSource(sourceId, sourceName, baseUrl, rules);
            BOOK_SOURCES.put(sourceId, bookSource);
            
            JSONObject resultObj = new JSONObject();
            resultObj.put("message", "书源添加成功");
            resultObj.put("sourceId", sourceId);
            resultObj.put("sourceName", sourceName);
            resultObj.put("baseUrl", baseUrl);
            resultObj.put("rulesCount", rules.size());
            return resultObj.toString();
        } catch (Exception e) {
            return new JSONObject()
                .put("error", "添加书源时发生错误: " + e.getMessage())
                .put("configUrl", configUrl)
                .toString();
        } finally {
            httpClient.close();
        }
    }
    
    /**
     * 安全获取JSON字段的字符串值
     */
    private String getStringValue(JSONObject config, String key) {
        try {
            if (config.has(key)) {
                Object value = config.get(key);
                if (value instanceof String) {
                    return (String) value;
                } else {
                    // 如果不是字符串，转换为字符串
                    return value.toString();
                }
            }
        } catch (Exception e) {
            // 忽略异常，返回空字符串
        }
        return "";
    }
    
    /**
     * 分析书源配置
     */
    @GetMapping("/analyzeSource")
    @Operation(summary = "分析书源配置", description = "分析指定URL的书源配置信息")
    public String analyzeSource(@Parameter(description = "书源配置URL") @RequestParam String configUrl) throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        
        try {
            HttpGet request = new HttpGet(configUrl);
            request.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
            
            CloseableHttpResponse response = httpClient.execute(request);
            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
            
            // 解析JSON配置
            JSONArray configs = new JSONArray(result);
            JSONObject config = configs.getJSONObject(0); // 取第一个配置
            
            JSONObject analysis = new JSONObject();
            
            // 基本信息
            analysis.put("bookSourceName", config.optString("bookSourceName", "N/A"));
            analysis.put("bookSourceUrl", config.optString("bookSourceUrl", "N/A"));
            analysis.put("bookSourceGroup", config.optString("bookSourceGroup", "N/A"));
            analysis.put("bookSourceType", config.optInt("bookSourceType", -1));
            analysis.put("enabled", config.optBoolean("enabled", false));
            analysis.put("customOrder", config.optInt("customOrder", 0));
            analysis.put("lastUpdateTime", config.optString("lastUpdateTime", "N/A"));
            
            // JS相关
            if (config.has("jsLib")) {
                analysis.put("hasJsLib", true);
                analysis.put("jsLibLength", config.getString("jsLib").length());
            } else {
                analysis.put("hasJsLib", false);
            }
            
            if (config.has("exploreUrl") && config.getString("exploreUrl").startsWith("@js:")) {
                analysis.put("hasJsExploreUrl", true);
            } else {
                analysis.put("hasJsExploreUrl", false);
            }
            
            // 登录相关
            if (config.has("loginUrl")) {
                analysis.put("hasLoginUrl", true);
            } else {
                analysis.put("hasLoginUrl", false);
            }
            
            if (config.has("loginUi")) {
                analysis.put("hasLoginUi", true);
            } else {
                analysis.put("hasLoginUi", false);
            }
            
            // 规则相关
            JSONObject rules = new JSONObject();
            if (config.has("searchUrl")) {
                rules.put("searchUrl", getStringValue(config, "searchUrl"));
            }
            if (config.has("ruleSearchList")) {
                rules.put("ruleSearchList", getStringValue(config, "ruleSearchList"));
            }
            if (config.has("ruleBookName")) {
                rules.put("ruleBookName", getStringValue(config, "ruleBookName"));
            }
            if (config.has("ruleBookAuthor")) {
                rules.put("ruleBookAuthor", getStringValue(config, "ruleBookAuthor"));
            }
            if (config.has("ruleBookCoverUrl")) {
                rules.put("ruleBookCoverUrl", getStringValue(config, "ruleBookCoverUrl"));
            }
            if (config.has("ruleBookIntro")) {
                rules.put("ruleBookIntro", getStringValue(config, "ruleBookIntro"));
            }
            if (config.has("ruleBookLastChapter")) {
                rules.put("ruleBookLastChapter", getStringValue(config, "ruleBookLastChapter"));
            }
            if (config.has("ruleBookTocUrl")) {
                rules.put("ruleBookTocUrl", getStringValue(config, "ruleBookTocUrl"));
            }
            if (config.has("ruleTocList")) {
                rules.put("ruleTocList", getStringValue(config, "ruleTocList"));
            }
            if (config.has("ruleTocName")) {
                rules.put("ruleTocName", getStringValue(config, "ruleTocName"));
            }
            if (config.has("ruleTocUrl")) {
                rules.put("ruleTocUrl", getStringValue(config, "ruleTocUrl"));
            }
            if (config.has("ruleContent")) {
                rules.put("ruleContent", getStringValue(config, "ruleContent"));
            }
            if (config.has("ruleContentNext")) {
                rules.put("ruleContentNext", getStringValue(config, "ruleContentNext"));
            }
            if (config.has("header")) {
                rules.put("header", getStringValue(config, "header"));
            }
            
            analysis.put("rules", rules);
            
            return analysis.toString();
        } finally {
            httpClient.close();
        }
    }
    
    /**
     * 测试书源搜索功能
     */
    @GetMapping("/testSearch")
    @Operation(summary = "测试书源搜索", description = "测试指定书源的搜索功能")
    public String testSearch(
            @Parameter(description = "搜索关键字") @RequestParam String keyword,
            @Parameter(description = "书源ID") @RequestParam String sourceId) throws IOException {
        
        BookSource bookSource = BOOK_SOURCES.get(sourceId);
        if (bookSource == null) {
            return new JSONObject().put("error", "未找到指定书源").toString();
        }
        
        // 验证书源URL
        String baseUrl = bookSource.getBaseUrl();
        try {
            URL url = new URL(baseUrl);
            String host = url.getHost();
            if (host == null || host.isEmpty()) {
                return new JSONObject().put("error", "书源URL格式不正确").toString();
            }
        } catch (Exception e) {
            return new JSONObject().put("error", "书源URL格式不正确: " + e.getMessage()).toString();
        }
        
        // 构造搜索URL
        String searchPath = bookSource.getRule("searchUrl", "/s?q=");
        String searchUrl;
        
        try {
            // 规范化基础URL，移除末尾的斜杠
            if (baseUrl.endsWith("/")) {
                baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
            }
            
            // 处理搜索路径中的参数替换
            if (searchPath.contains("{{key}}") || searchPath.contains("{{page}}")) {
                // 处理模板语法 {{key}} 和 {{page}}
                searchPath = searchPath.replace("{{key}}", URLEncoder.encode(keyword, "UTF-8"))
                                      .replace("{{page}}", "1");
                searchUrl = buildUrl(baseUrl, searchPath);
            } else if (searchPath.contains("{key}") || searchPath.contains("{page}")) {
                // 处理模板语法 {key} 和 {page}
                searchPath = searchPath.replace("{key}", URLEncoder.encode(keyword, "UTF-8"))
                                      .replace("{page}", "1");
                searchUrl = buildUrl(baseUrl, searchPath);
            } else if (searchPath.contains("?")) {
                // 处理标准查询参数
                String encodedKeyword = URLEncoder.encode(keyword, "UTF-8");
                
                // 检查是否包含key或keyword参数占位符
                if (searchPath.contains("key=")) {
                    // 替换key参数占位符
                    searchPath = searchPath.replace("key=", "key=" + encodedKeyword);
                } else if (searchPath.contains("keyword=")) {
                    // 替换keyword参数占位符
                    searchPath = searchPath.replace("keyword=", "keyword=" + encodedKeyword);
                } else {
                    // 添加keyword参数
                    String separator = searchPath.endsWith("?") ? "" : "&";
                    searchPath = searchPath + separator + "keyword=" + encodedKeyword;
                }
                
                searchUrl = buildUrl(baseUrl, searchPath);
            } else {
                // 处理路径参数
                String encodedKeyword = URLEncoder.encode(keyword, "UTF-8");
                searchPath = searchPath + encodedKeyword;
                searchUrl = buildUrl(baseUrl, searchPath);
            }
        } catch (Exception e) {
            return new JSONObject()
                .put("error", "URL编码错误: " + e.getMessage())
                .put("baseUrl", baseUrl)
                .put("searchPath", searchPath)
                .toString();
        }
        
        // 使用HttpClient发送请求
        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpGet request = new HttpGet(searchUrl);
            request.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
            request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
            request.addHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
            request.addHeader("Connection", "keep-alive");
            request.addHeader("Upgrade-Insecure-Requests", "1");
            request.addHeader("Sec-Fetch-Dest", "document");
            request.addHeader("Sec-Fetch-Mode", "navigate");
            request.addHeader("Sec-Fetch-Site", "none");
            request.addHeader("Cache-Control", "max-age=0");
            
            CloseableHttpResponse response = httpClient.execute(request);
            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
            
            JSONObject testResult = new JSONObject();
            testResult.put("searchUrl", searchUrl);
            testResult.put("responseLength", result.length());
            testResult.put("responseCode", response.getStatusLine().getStatusCode());
            
            // 截取前1000个字符作为示例
            String preview = result.length() > 1000 ? result.substring(0, 1000) : result;
            testResult.put("responsePreview", preview);
            
            return testResult.toString();
        } finally {
            httpClient.close();
        }
    }
    
    /**
     * 获取书籍目录接口
     */
    @GetMapping("/toc")
    @Operation(summary = "获取书籍目录", description = "根据书籍目录URL和书源获取书籍的所有章节")
    public String getBookToc(
            @Parameter(description = "书籍目录URL") @RequestParam String tocUrl,
            @Parameter(description = "书源ID") @RequestParam(required = false, defaultValue = "default") String sourceId) throws IOException {
        
        BookSource bookSource = BOOK_SOURCES.get(sourceId);
        if (bookSource == null) {
            return new JSONObject().put("error", "未找到指定书源").toString();
        }
        
        // 验证目录URL
        try {
            URL url = new URL(tocUrl);
            String host = url.getHost();
            if (host == null || host.isEmpty()) {
                return new JSONObject().put("error", "目录URL格式不正确").toString();
            }
        } catch (Exception e) {
            return new JSONObject().put("error", "目录URL格式不正确: " + e.getMessage()).toString();
        }
        
        // 使用HttpClient发送请求
        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpGet request = new HttpGet(tocUrl);
            request.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
            request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
            request.addHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
            request.addHeader("Connection", "keep-alive");
            request.addHeader("Upgrade-Insecure-Requests", "1");
            request.addHeader("Sec-Fetch-Dest", "document");
            request.addHeader("Sec-Fetch-Mode", "navigate");
            request.addHeader("Sec-Fetch-Site", "none");
            request.addHeader("Cache-Control", "max-age=0");
            
            CloseableHttpResponse response = httpClient.execute(request);
            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
            Document doc = Jsoup.parse(result);
            
            // 解析目录
            JSONArray chapters = new JSONArray();
            
            // 使用书源配置的目录列表规则
            String chapterListSelector = bookSource.getRule("toc.chapterList", "a[href*=chapter]:not([href*=javascript])");
            Elements chapterElements = doc.select(chapterListSelector);
            
            // 如果使用默认选择器没有找到结果，尝试其他常见选择器
            if (chapterElements.isEmpty()) {
                // 尝试多种常见的章节链接选择器
                String[] fallbackSelectors = {
                    ".chapter-list a:not([href*=javascript])",
                    ".catalog a:not([href*=javascript])",
                    ".directory a:not([href*=javascript])",
                    ".chapters a:not([href*=javascript])",
                    ".list a:not([href*=javascript])",
                    "a[href*=chapter]:not([href*=javascript])",
                    "a[href*=read]:not([href*=javascript])",
                    ".chapter-item a:not([href*=javascript])",
                    ".chapter a:not([href*=javascript])",
                    "dd a:not([href*=javascript])",
                    "li a:not([href*=javascript])",
                    "a:not([href*=javascript])"
                };
                
                for (String selector : fallbackSelectors) {
                    chapterElements = doc.select(selector);
                    // 过滤掉明显不是章节的链接
                    Elements filteredElements = new Elements();
                    for (Element element : chapterElements) {
                        String text = element.text().trim();
                        // 过滤掉导航链接
                        if (!text.equals("目录") && !text.equals("更多") && !text.equals("查看更多") && 
                            !text.equals("返回") && !text.equals("首页") && !text.equals("书架") &&
                            !text.toLowerCase().contains("menu") && !text.toLowerCase().contains("catalog")) {
                            filteredElements.add(element);
                        }
                    }
                    
                    if (!filteredElements.isEmpty()) {
                        chapterElements = filteredElements;
                        chapterListSelector = selector + " (fallback)";
                        break;
                    }
                }
            } else {
                // 过滤掉明显不是章节的链接
                Elements filteredElements = new Elements();
                for (Element element : chapterElements) {
                    String text = element.text().trim();
                    // 过滤掉导航链接
                    if (!text.equals("目录") && !text.equals("更多") && !text.equals("查看更多") && 
                        !text.equals("返回") && !text.equals("首页") && !text.equals("书架") &&
                        !text.toLowerCase().contains("menu") && !text.toLowerCase().contains("catalog")) {
                        filteredElements.add(element);
                    }
                }
                chapterElements = filteredElements;
            }
            
            // 如果仍然没有找到元素，尝试查找所有链接并进行过滤
            if (chapterElements.isEmpty()) {
                Elements allLinks = doc.select("a[href]");
                for (Element link : allLinks) {
                    String href = link.attr("href");
                    String text = link.text().trim();
                    
                    // 判断是否可能是章节链接
                    if (isLikelyChapterLink(href, text)) {
                        chapterElements.add(link);
                    }
                }
                
                if (!chapterElements.isEmpty()) {
                    chapterListSelector = "custom filter (all links filtered)";
                }
            }
            
            for (Element element : chapterElements) {
                String chapterName = element.text().trim();
                String chapterUrl = element.attr("href");
                
                // 过滤无效章节名称
                if (chapterName.isEmpty() || chapterName.length() <= 1 || 
                    chapterName.equals("目录") || chapterName.equals("更多") || 
                    chapterName.equals("查看更多") || chapterName.equals("返回") || 
                    chapterName.equals("首页") || chapterName.equals("书架") ||
                    chapterName.toLowerCase().contains("menu") || 
                    chapterName.toLowerCase().contains("catalog")) {
                    continue;
                }
                
                // 处理URL
                if (chapterUrl.startsWith("/")) {
                    // 相对路径，需要拼接基础URL
                    URL baseUrl = new URL(tocUrl);
                    chapterUrl = baseUrl.getProtocol() + "://" + baseUrl.getHost() + chapterUrl;
                } else if (chapterUrl.startsWith("#") || chapterUrl.startsWith("javascript:")) {
                    // 无效链接，跳过
                    continue;
                } else if (!chapterUrl.startsWith("http")) {
                    // 相对路径，需要拼接目录URL路径
                    URL baseUrl = new URL(tocUrl);
                    String basePath = baseUrl.getPath();
                    int lastSlash = basePath.lastIndexOf('/');
                    if (lastSlash > 0) {
                        basePath = basePath.substring(0, lastSlash + 1);
                    } else {
                        basePath = "/";
                    }
                    chapterUrl = baseUrl.getProtocol() + "://" + baseUrl.getHost() + basePath + chapterUrl;
                }
                
                JSONObject chapter = new JSONObject();
                chapter.put("name", chapterName);
                chapter.put("url", chapterUrl);
                chapters.put(chapter);
            }
            
            JSONObject resultObj = new JSONObject();
            resultObj.put("chapters", chapters);
            resultObj.put("count", chapters.length());
            resultObj.put("sourceId", sourceId);
            resultObj.put("tocUrl", tocUrl);
            resultObj.put("usedSelector", chapterListSelector);
            resultObj.put("totalElementsFound", chapterElements.size());
            resultObj.put("responseLength", result.length());
            
            return resultObj.toString();
        } catch (Exception e) {
            return new JSONObject()
                .put("error", "获取目录过程中发生错误: " + e.getMessage())
                .put("tocUrl", tocUrl)
                .toString();
        } finally {
            httpClient.close();
        }
    }
    
    /**
     * 获取章节内容接口
     */
    @GetMapping("/content")
    @Operation(summary = "获取章节内容", description = "根据章节URL和书源获取章节的具体内容")
    public String getChapterContent(
            @Parameter(description = "章节内容URL") @RequestParam String contentUrl,
            @Parameter(description = "书源ID") @RequestParam(required = false, defaultValue = "default") String sourceId) throws IOException {
        
        BookSource bookSource = BOOK_SOURCES.get(sourceId);
        if (bookSource == null) {
            return new JSONObject().put("error", "未找到指定书源").toString();
        }
        
        // 验证内容URL
        try {
            URL url = new URL(contentUrl);
            String host = url.getHost();
            if (host == null || host.isEmpty()) {
                return new JSONObject().put("error", "内容URL格式不正确").toString();
            }
        } catch (Exception e) {
            return new JSONObject().put("error", "内容URL格式不正确: " + e.getMessage()).toString();
        }
        
        // 使用HttpClient发送请求
        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpGet request = new HttpGet(contentUrl);
            request.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
            request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
            request.addHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
            request.addHeader("Connection", "keep-alive");
            request.addHeader("Upgrade-Insecure-Requests", "1");
            request.addHeader("Sec-Fetch-Dest", "document");
            request.addHeader("Sec-Fetch-Mode", "navigate");
            request.addHeader("Sec-Fetch-Site", "none");
            request.addHeader("Cache-Control", "max-age=0");
            
            CloseableHttpResponse response = httpClient.execute(request);
            String result = EntityUtils.toString(response.getEntity(), "UTF-8");
            Document doc = Jsoup.parse(result);
            
            // 使用书源配置的内容规则提取章节内容
            String contentSelector = bookSource.getRule("content.content", ".content");
            
            // 检查是否是JavaScript规则
            String contentText = "";
            String contentSelectorUsed = contentSelector;
            
            if (contentSelector.startsWith("@js:") || contentSelector.startsWith("<js>")) {
                // 这是JavaScript规则，返回原始内容供客户端处理
                contentText = contentSelector;
                contentSelectorUsed = "JavaScript rule";
            } else {
                // 普通CSS选择器规则
                Elements contentElements = doc.select(contentSelector);
                
                // 如果使用默认选择器没有找到结果，尝试其他常见选择器
                if (contentElements.isEmpty()) {
                    // 尝试多种常见的内容选择器
                    String[] fallbackSelectors = {
                        ".Readarea",
                        ".read-content",
                        ".content",
                        ".chapter-content",
                        ".article-content",
                        ".novel-content",
                        "#content",
                        ".txt",
                        "div[class*=read]",
                        "div[class*=content]"
                    };
                    
                    for (String selector : fallbackSelectors) {
                        contentElements = doc.select(selector);
                        if (!contentElements.isEmpty()) {
                            contentSelector = selector;
                            contentSelectorUsed = selector + " (fallback)";
                            break;
                        }
                    }
                }
                
                StringBuilder contentBuilder = new StringBuilder();
                for (Element element : contentElements) {
                    // 获取元素的HTML内容并保留换行
                    String htmlContent = element.html();
                    // 将HTML中的<br>标签替换为换行符
                    htmlContent = htmlContent.replaceAll("<br\\s*/?>", "\n");
                    // 移除其他HTML标签
                    String textContent = Jsoup.parse(htmlContent).text();
                    contentBuilder.append(textContent).append("\n\n");
                }
                
                // 如果仍然没有找到内容，尝试查找包含"content"或"text"的div元素
                if (contentBuilder.length() == 0) {
                    Elements allDivs = doc.select("div");
                    for (Element div : allDivs) {
                        String className = div.className().toLowerCase();
                        String id = div.id().toLowerCase();
                        if (className.contains("content") || className.contains("text") || 
                            id.contains("content") || id.contains("text")) {
                            String htmlContent = div.html();
                            htmlContent = htmlContent.replaceAll("<br\\s*/?>", "\n");
                            String textContent = Jsoup.parse(htmlContent).text();
                            contentBuilder.append(textContent).append("\n\n");
                            contentSelector = "div[class*=" + className + " or id*=" + id + "]";
                            contentSelectorUsed = contentSelector + " (custom)";
                            break;
                        }
                    }
                }
                
                contentText = contentBuilder.toString().trim();
            }
            
            // 获取下一章链接
            String nextChapterSelector = bookSource.getRule("content.nextPage", "a[href*=next]");
            String nextChapterUrl = "";
            String nextChapterSelectorUsed = nextChapterSelector;
            
            // 检查是否是JavaScript规则
            if (nextChapterSelector.startsWith("@js:") || nextChapterSelector.startsWith("<js>")) {
                // 这是JavaScript规则，返回原始规则供客户端处理
                nextChapterUrl = nextChapterSelector;
                nextChapterSelectorUsed = "JavaScript rule";
            } else {
                // 普通CSS选择器规则
                Elements nextChapterElements = doc.select(nextChapterSelector);
                
                // 如果使用默认选择器没有找到结果，尝试其他常见选择器
                if (nextChapterElements.isEmpty()) {
                    String[] fallbackNextSelectors = {
                        "a:contains(下一页)",
                        "a:contains(下一章)",
                        "a:contains(下章)",
                        "a[href*=next]",
                        "a[href*=chapter]",
                        ".next a",
                        ".next-page a"
                    };
                    
                    for (String selector : fallbackNextSelectors) {
                        nextChapterElements = doc.select(selector);
                        if (!nextChapterElements.isEmpty()) {
                            nextChapterSelector = selector;
                            nextChapterSelectorUsed = selector + " (fallback)";
                            break;
                        }
                    }
                }
                
                // 处理下一章URL
                if (!nextChapterElements.isEmpty()) {
                    Element nextElement = nextChapterElements.first();
                    nextChapterUrl = nextElement.attr("href");
                    
                    // 处理相对路径
                    if (nextChapterUrl.startsWith("/")) {
                        // 相对路径，需要拼接基础URL
                        URL baseUrl = new URL(contentUrl);
                        nextChapterUrl = baseUrl.getProtocol() + "://" + baseUrl.getHost() + nextChapterUrl;
                    } else if (!nextChapterUrl.startsWith("http")) {
                        // 相对路径，需要拼接内容URL路径
                        URL baseUrl = new URL(contentUrl);
                        String basePath = baseUrl.getPath();
                        int lastSlash = basePath.lastIndexOf('/');
                        if (lastSlash > 0) {
                            basePath = basePath.substring(0, lastSlash + 1);
                        } else {
                            basePath = "/";
                        }
                        nextChapterUrl = baseUrl.getProtocol() + "://" + baseUrl.getHost() + basePath + nextChapterUrl;
                    }
                }
            }
            
            JSONObject resultObj = new JSONObject();
            resultObj.put("content", contentText);
            resultObj.put("contentLength", contentText.length());
            resultObj.put("sourceId", sourceId);
            resultObj.put("contentUrl", contentUrl);
            resultObj.put("usedContentSelector", contentSelectorUsed);
            resultObj.put("usedNextChapterSelector", nextChapterSelectorUsed);
            resultObj.put("nextChapterUrl", nextChapterUrl);
            resultObj.put("responseLength", result.length());
            resultObj.put("isJavaScriptRule", contentSelector.startsWith("@js:") || contentSelector.startsWith("<js>") ||
                                              nextChapterSelector.startsWith("@js:") || nextChapterSelector.startsWith("<js>"));
            
            return resultObj.toString();
        } catch (Exception e) {
            return new JSONObject()
                .put("error", "获取章节内容过程中发生错误: " + e.getMessage())
                .put("contentUrl", contentUrl)
                .toString();
        } finally {
            httpClient.close();
        }
    }
    
    /**
     * 判断链接是否可能是章节链接
     */
    private boolean isLikelyChapterLink(String href, String text) {
        // 过滤明显不是章节链接的内容
        if (text.isEmpty() || text.length() <= 1) {
            return false;
        }
        
        // 过滤导航链接
        if (text.equals("目录") || text.equals("更多") || text.equals("查看更多") || 
            text.equals("返回") || text.equals("首页") || text.equals("书架") ||
            text.toLowerCase().contains("menu") || text.toLowerCase().contains("catalog")) {
            return false;
        }
        
        // 过滤无效链接
        if (href.startsWith("#") || href.startsWith("javascript:")) {
            return false;
        }
        
        // 检查链接是否包含章节相关关键词
        if (href.contains("chapter") || href.contains("read") || 
            href.contains("novel") || href.contains("content")) {
            return true;
        }
        
        // 检查文本是否看起来像章节标题
        if (text.length() >= 2 && (text.matches(".*第.+章.*") || 
                                   text.matches(".*[0-9]+.*") || 
                                   text.contains("序章") || 
                                   text.contains("序言") || 
                                   text.contains("前言") || 
                                   text.contains("后记") || 
                                   text.contains("尾声") || 
                                   text.contains("番外"))) {
            return true;
        }
        
        return false;
    }
}